/*  -*- mode: c++; coding: utf-8; c-file-style: "stroustrup"; -*-

    Copyright 2008 Asier Aguirre <asier.aguirre@gmail.com>
    This file is part of Intromit.

    Intromit is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Intromit is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Intromit.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef PYMETADATA_GLOBAL_HH
#define PYMETADATA_GLOBAL_HH
//! \addtogroup python_backend
//@{

// ENUMS
enum marshalling_return_type
{
    PYMD_MARSHALLING_RETURN_VOID,
    PYMD_MARSHALLING_RETURN_INT,
    PYMD_MARSHALLING_RETURN_INT_DONOTFREE,
    PYMD_MARSHALLING_RETURN_LONG_LONG,
    PYMD_MARSHALLING_RETURN_FLOAT,
    PYMD_MARSHALLING_RETURN_DOUBLE,
    PYMD_MARSHALLING_RETURN_LAST
};

// DECLARATIONS
class python_metadata;
class python_type_metadata;
class pymetadata_global;

// TYPEDEFS
// type pointer: C++ object member absolute address
// data: metadata address
// value: python object
typedef PyObject *(*md_c_to_python_func)(void *type_pointer, int *data);           // returns NULL in case of error
typedef int (*md_python_to_c_func)(PyObject *value, void *type_pointer, int *data);// returns -1 in case of error
typedef int (*md_check_python_func)(PyObject *value, int *data);                   // returns 0 in case of error
typedef int (*md_free_c_func)(void *type_pointer, int *data);                      // returns 0 in case of error

// static object
extern pymetadata_global pymdg;

// CLASS

//! Registers modules, metadata and python objects
class pymetadata_global
{
    // stores dynamic memory from types to be removed at module exit

public:
    struct metadata
    {
	int *data;
	int n_data;
	char *doc;
	int n_doc;
	unsigned checksum;
    };

    struct dl_handles
    {
	char *elf_name;
	void *dl;
    };

    struct type_function
    {
	md_c_to_python_func c_to_python;
	md_python_to_c_func python_to_c;
	md_check_python_func check_python;
	md_free_c_func free_c;
	marshalling_return_type return_marshalling;
    };

    struct event_queue_object
    {
	python_metadata *obj;
	char *func_name;
	int (*func)(void *obj);
    };

    struct event_queue
    {
	int i_queue_obj;
	unsigned time;
    };

private:

    // metadata
    metadata *meta;
    int n_meta, max_meta;

    // python types (sorted by ascending vtable)
    python_type_metadata **type;
    int n_type, max_type, first_polymorphic_type;

    // special types
    python_type_metadata *type_python_vector;

    // dl_handles
    dl_handles *dl;
    int n_dl, max_dl;

    // functions
    type_function *func;

    // python_metadata object list (sorted by python_metadata::obj)
    python_metadata **obj_list;
    int n_obj_list, max_obj_list, n_obj_list_vanished;

    // event queue
    event_queue_object *eq_obj;
    int n_eq_obj, max_eq_obj;
    event_queue *eq;
    int eq_ini, eq_end; // eq_ini > eq_end; eq_end event will be taken out first

    // error strings
    char *error_string;
    int n_error_string, max_error_string;

    // PRIVATE METHODS
    void dtor();

    // type functions
    // float
    static PyObject *c_to_python_float(void *type_pointer, int *data);
    static int python_to_c_float(PyObject *value, void *type_pointer, int *data);
    static int check_python_float(PyObject *value, int *data);
    // double
    static PyObject *c_to_python_double(void *type_pointer, int *data);
    static int python_to_c_double(PyObject *value, void *type_pointer, int *data);
    static int check_python_double(PyObject *value, int *data);
    // int
    static PyObject *c_to_python_int(void *type_pointer, int *data);
    static int python_to_c_int(PyObject *value, void *type_pointer, int *data);
    static int check_python_int(PyObject *value, int *data);
    // unsigned int
    static PyObject *c_to_python_unsigned_int(void *type_pointer, int *data);
    static int python_to_c_unsigned_int(PyObject *value, void *type_pointer, int *data);
    static int check_python_unsigned_int(PyObject *value, int *data);
    // short
    static PyObject *c_to_python_short(void *type_pointer, int *data);
    static int python_to_c_short(PyObject *value, void *type_pointer, int *data);
    static int check_python_short(PyObject *value, int *data);
    // unsigned short
    static PyObject *c_to_python_unsigned_short(void *type_pointer, int *data);
    static int python_to_c_unsigned_short(PyObject *value, void *type_pointer, int *data);
    static int check_python_unsigned_short(PyObject *value, int *data);
    // long long
    static PyObject *c_to_python_long_long(void *type_pointer, int *data);
    static int python_to_c_long_long(PyObject *value, void *type_pointer, int *data);
    static int check_python_long_long(PyObject *value, int *data);
    // ulong long
    static PyObject *c_to_python_ulong_long(void *type_pointer, int *data);
    static int python_to_c_ulong_long(PyObject *value, void *type_pointer, int *data);
    static int check_python_ulong_long(PyObject *value, int *data);
    // char
    static PyObject *c_to_python_char(void *type_pointer, int *data);
    static int python_to_c_char(PyObject *value, void *type_pointer, int *data);
    static int check_python_char(PyObject *value, int *data);
    // unsigned char
    static PyObject *c_to_python_unsigned_char(void *type_pointer, int *data);
    static int python_to_c_unsigned_char(PyObject *value, void *type_pointer, int *data);
    static int check_python_unsigned_char(PyObject *value, int *data);
    // object pointer
    static int common_object_pointer(int *data, python_type_metadata **type, int **data_obj, int *has_reference=NULL);
    static PyObject *c_to_python_object_pointer(void *type_pointer, int *data);
    static int python_to_c_object_pointer(PyObject *value, void *type_pointer, int *data);
    static int check_python_object_pointer(PyObject *value, int *data);
    static int free_c_object_pointer(void *type_pointer, int *data);
    // class
    static PyObject *c_to_python_class(void *type_pointer, int *data);
    static int python_to_c_class(PyObject *value, void *type_pointer, int *data);
    static int check_python_class(PyObject *value, int *data);
    // reference
    static int common_reference(int *data, int **data_obj);
    static PyObject *c_to_python_reference(void *type_pointer, int *data);
    static int python_to_c_reference(PyObject *value, void *type_pointer, int *data);
    static int check_python_reference(PyObject *value, int *data);
    static int free_c_reference(void *type_pointer, int *data);
    // charp
    static PyObject *c_to_python_charp(void *type_pointer, int *data);
    static int python_to_c_charp(PyObject *value, void *type_pointer, int *data);
    static int check_python_charp(PyObject *value, int *data);    
    static int free_c_charp(void *type_pointer, int *data);
    // const charp
    static PyObject *c_to_python_const_charp(void *type_pointer, int *data);
    static int python_to_c_const_charp(PyObject *value, void *type_pointer, int *data);
    static int check_python_const_charp(PyObject *value, int *data);    
    // array_type
    static int common_array_type(int *data);
    static PyObject *c_to_python_array_type(void *type_pointer, int *data);
    static int python_to_c_array_type(PyObject *value, void *type_pointer, int *data);
    static int check_python_array_type(PyObject *value, int *data);    
    static int free_c_array_type(void *type_pointer, int *data);
    // int
    static PyObject *c_to_python_boolean(void *type_pointer, int *data);
    static int python_to_c_boolean(PyObject *value, void *type_pointer, int *data);
    static int check_python_boolean(PyObject *value, int *data);

    // python object list
    int search_object_index(void *obj);
    // in case of multiple addr coincidence, i_destination_if_added is the pos of the first coincidence
    python_metadata *search_object(void *obj, int *i_destination_if_added=NULL, int *n_at_addr=NULL);
    static int sort_object_list_by_woind(const void *a, const void *b);

    // event queue
    void eq_extract_first();
    void eq_add(int i_queue_obj, unsigned time);
    void eq_del(int position);
    static unsigned eq_time_now();
    static bool eq_time_compare(unsigned time1, unsigned time2); // returns true if time1 >= time2

    // python type metadata
    static int sort_type_by_vtable_compar(const void *a, const void *b);

public:
    pymetadata_global();
    ~pymetadata_global();

    // returns the index to meta struct array/dl handle or -1 if it does not exists
    int meta_exists(int *data, int n_data, char *doc, int n_doc);
    int dl_handle_exists(char *elf_name);

    // add
    int add_meta(int *data, int n_data, char *doc, int n_doc);
    int add_type(python_type_metadata *type);
    int add_dl(char *elf_name, void *dl);

    // python type metadata
    void sort_type_by_vtable();
    python_type_metadata *search_type_by_vtable(void **vtable);
    bool check_type(python_type_metadata *type); // return true if registered
    inline python_type_metadata *get_type_python_vector() {return type_python_vector;}
    void dump_type_list();

    //! @name Getters
    //@{
    inline metadata *get_meta(int i) {return &meta[i];}
    inline int get_n_type() {return n_type;}
    inline python_type_metadata *get_type(int i) {return type[i];}
    inline dl_handles *get_dl_handle(int i) {return &dl[i];}
    inline marshalling_return_type get_return_marshalling_type(int gendata_type) {return func[gendata_type].return_marshalling;}
    inline md_c_to_python_func get_func_c_to_python(int gendata_type) {return func[gendata_type].c_to_python;}
    inline md_python_to_c_func get_func_python_to_c(int gendata_type) {return func[gendata_type].python_to_c;}
    inline md_check_python_func get_func_check_python(int gendata_type) {return func[gendata_type].check_python;}
    inline md_free_c_func get_func_free_c(int gendata_type) {return func[gendata_type].free_c;} //@}

    //! @name Python object list
    //@{
    // obj must have python_metadata::obj already set
    // only call this function if object is not on list!
    // returns -1 in case of error
    int add_object(python_metadata *obj, int i_destination=-1);
    // add an array of objects, the first object will determine the poisition of the rest
    // returns -1 in case of error
    int add_objects(python_metadata **obj, int n_obj);
    // search python_metadata object by its python_metadata::obj
    // in case of multiple addr coincidence, i_destination_if_added is the pos of the returned obj
    // and n_at_addr is the objects at the same addr after the returned obj incluing itself
    python_metadata *search_object(void *obj, python_type_metadata *type,
				   int *i_destination_if_added=NULL, int *n_at_addr=NULL);
    // returns the index of pyobj or -1 if does not exists
    int search_object(python_metadata *pyobj);
    // search python_metadata object container
    python_metadata *search_object_container(void *obj_contained, int *i_destination_if_added=NULL);
    // returns 0 in case of error
    int remove_object(python_metadata *python_obj);
    int remove_object(int i, int n=1, python_metadata **copy_them_here=NULL);
    // both i_destination and i_origin are positions before the movement
    void move_objects(int i_destination, int i_origin, int n);
    // count contained objects of object at position i_obj (i_obj not counted)
    int n_contained_objects(int i_obj);
    void dump_object_list(python_metadata *obj=NULL);
    inline python_metadata *get_obj_list(int i) {return obj_list[i];} //@}
    // checks that object list is sorted
    bool check_object_list();
    // force sorting of object list
    void sort_object_list();

    //! @name Event queue
    //@{
    // add an event to queue: func must be non-static and obj, ref. counted
    bool event_queue_add(python_metadata *obj, const char *method_name);
    bool event_queue_remove(python_metadata *obj=NULL, const char *method_name=NULL);
    bool event_queue_refresh(bool control_idle_time); //@}

    //! @name Error strings
    //@{
    void clear_error_string();
    void add_error_string(const char *format, ...);
    inline char *get_error_string() {return error_string;}
    static char *python_error_string(PyObject **exception=NULL); //@}
};

//@}
#endif

/* Offline metadata attributes:

   @metadata_attrs(
	pymdg,
	pymetadata_global,
	marshalling_return_type,
	md_c_to_python_func,
	md_python_to_c_func,
	md_check_python_func,
	md_free_c_func:
					~select
   )
*/
